//
//  ConcurrentOperation.m
//  多线程编程
//
//  Created by GR on 2017/7/16.
//  Copyright © 2017年 GR Harry. All rights reserved.
//

#import "ConcurrentOperation.h"

@interface ConcurrentOperation (){
    BOOL        executing;  // 执行中
    BOOL        finished;   // 已完成
}
@property (copy, nonatomic) OperationBlock block;
@end

@implementation ConcurrentOperation

- (instancetype)initWithOperationBlock:(OperationBlock)block {
    if (self = [super init]) {
        self.block = block;
    }
    return self;
}

// 必须重写
- (void)start {
    if ([self isCancelled]) {
        [self willChangeValueForKey:@"isFinished"];
        finished = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }
    
    [self willChangeValueForKey:@"isExecuting"];
    [self main];
    executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
}

// 必须重写
- (void)main {
    
    @try {
        @autoreleasepool {
            if ([self isCancelled]) {
                [self completeOperation];
                return;
            }
            
            self.block( ^{
                [self completeOperation];
            } );
        }
    } @catch (NSException *exception) {
        NSLog(@"exception - %@", exception);
    }
    
    
}

// 必须重写
- (BOOL)isAsynchronous {
    return YES;
}

// 必须重写
- (BOOL)isExecuting {
    return executing;
}

// 必须重写
- (BOOL)isFinished {
    return finished;
}

- (void)completeOperation {
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];
    
    executing = NO;
    finished = YES;
    
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

- (BOOL)startOperation:(NSOperation *)anOp {
    BOOL        ranIt = NO;
    
    if ([anOp isReady] && ![anOp isCancelled])
    {
        if (![anOp isAsynchronous]) {
            [anOp start];
        }
        else {
            [NSThread detachNewThreadSelector:@selector(start)
                                     toTarget:anOp withObject:nil];
        }
        ranIt = YES;
    }
    else if ([anOp isCancelled])
    {
        // If it was canceled before it was started,
        //  move the operation to the finished state.
        [self willChangeValueForKey:@"isFinished"];
        [self willChangeValueForKey:@"isExecuting"];
        executing = NO;
        finished = YES;
        [self didChangeValueForKey:@"isExecuting"];
        [self didChangeValueForKey:@"isFinished"];
        
        // Set ranIt to YES to prevent the operation from
        // being passed to this method again in the future.
        ranIt = YES;
    }
    return ranIt;
}

@end
